package xfuzzy.xfghl.codification;

import java.util.ArrayList;
import java.util.List;
import java.util.Random;

import xfuzzy.xfghl.model.XfghlConfig;

/**
 * Clase que repara los cromosomas con errores
 * @author Alberto David Fernandez Fernandez
 * */

public class ChromosomeFixer implements EncodingSymbols {
	
	/**
	 * Metodo que repara un cromosoma no valido
	 * @param cromosoma Cromosoma con errores
	 * @param config Configuracion de la herramienta xfghl
	 * */

	public static void reparar(DoubleChromosome cromosoma, XfghlConfig config)  {
		
		TypeErrorChromosome error = ChromosomeTester.isValid(cromosoma);
		while (error != null)  {
			if (error == TypeErrorChromosome.ERROR1)
				ChromosomeFixer.repararError1(cromosoma, config);
			else if (error == TypeErrorChromosome.ERROR2)
				ChromosomeFixer.repararError2(cromosoma);
			else if (error == TypeErrorChromosome.ERROR3)
				ChromosomeFixer.repararError3(cromosoma, config);
			else if (error == TypeErrorChromosome.ERROR4)
				ChromosomeFixer.repararError4(cromosoma);
			else if (error == TypeErrorChromosome.ERROR5)
				ChromosomeFixer.repararError5(cromosoma);
			else
				ChromosomeFixer.repararError6(cromosoma);
			error = ChromosomeTester.isValid(cromosoma);
		}
	}
	
	/**
	 * Metodo que repara un cromosoma no valido
	 * @param error Tipo de error del cromosoma
	 * @param cromosoma Cromosoma con errores
	 * @param config Configuracion de la herramienta xfghl
	 * */
	
	public static void reparar(TypeErrorChromosome error, DoubleChromosome cromosoma, XfghlConfig config)  {
		
		if (error == TypeErrorChromosome.ERROR1)
			ChromosomeFixer.repararError1(cromosoma, config);
		else if (error == TypeErrorChromosome.ERROR2)
			ChromosomeFixer.repararError2(cromosoma);
		else if (error == TypeErrorChromosome.ERROR3)
			ChromosomeFixer.repararError3(cromosoma, config);
		else if (error == TypeErrorChromosome.ERROR4)
			ChromosomeFixer.repararError4(cromosoma);
		else if (error == TypeErrorChromosome.ERROR5)
			ChromosomeFixer.repararError5(cromosoma);
		else
			ChromosomeFixer.repararError6(cromosoma);
	}
	
	/**
	 * Metodo que repara un cromosoma con un numero inadecuado de variables en la codificacion
	 * @param cromosoma Cromosoma con errores
	 * @param config Configuracion de la herramienta xfghl
	 * */
	
	private static void repararError1(DoubleChromosome cromosoma, XfghlConfig config)  {
		
		// El numero de variables debe ser igual al valor de la variable "numVariables"
		int contador = 0, diferencia = 0, valor = -1;
		Random rnd = new Random(config.getReparationSeed());
		char [] estructura = cromosoma.getEstructura();
		for (int index = 0; index < estructura.length; index++)  {
			 if (estructura[index] == variableSymbol)
				 contador++;
		}
		// se calcula la diferencia entre las variables que hay y las que deberia haber
		diferencia = contador - cromosoma.getNumVariables();
		for (int index = 0; index < diferencia; index++)  {
			 // aqui se controla que no se eliga una posicion que pertenezca a las partes fijas
			 valor = 1 + rnd.nextInt(estructura.length - 2);
			 while (estructura[valor] == moduleSymbol)
				 valor = 1 + rnd.nextInt(estructura.length - 2);
			 // se cambia la posicion donde habia una variables por un modulo
			 estructura[valor] = moduleSymbol;
		}
	}
	
	/**
	 * Metodo que repara un cromosoma con variables repetidas en la codificacion
	 * @param cromosoma Cromosoma con errores
	 * */
	
	private static void repararError2(DoubleChromosome cromosoma)  {
		
		// Una o mas variables estan repetidas
		int [] contadorVariables = new int[cromosoma.getNumVariables()];
		int [] variables = cromosoma.getVariables();
		// creamos una lista de las variables posibles
		List<Integer> var_disponibles = new ArrayList<Integer>();
		for (int index = 0; index < cromosoma.getNumVariables(); index++)  {
			 var_disponibles.add(new Integer(index));
		}
		
		// recorremos la lista y dejamos en ella solo las variables que no aparecen
		Integer aux = null;
		for (int index = 0; index < variables.length; index++)  {
			 aux = new Integer(variables[index]);
			 if (var_disponibles.contains(aux))  {
				 var_disponibles.remove(aux);
			 }
		}
		
		// inicializamos el vector que contara el numero de apariciones de cada variable
		for (int i = 0; i < contadorVariables.length; i++)
			 contadorVariables[i] = 0;
		
		// contamos el numero de apariciones de cada variables; cuando sea mayor de cero, se sustituye por una variable que no aparece
		for (int i = 0; i < contadorVariables.length; i++)  {
			 if (contadorVariables[variables[i]] == 0)
				 contadorVariables[variables[i]]++;
			 else
				 variables[i] = var_disponibles.remove(0);
		}
	}
	
	/**
	 * Metodo que repara un cromosoma con un numero inadecuado de modulos en la codificacion
	 * @param cromosoma Cromosoma con errores
	 * @param config Configuracion de la herramienta xfghl
	 * */
	
	private static void repararError3(DoubleChromosome cromosoma, XfghlConfig config)  {
		
		// El numero de modulos no es igual al valor de la variable "numModulos"
		int contador = 0, diferencia = 0, valor = -1;
		Random rnd = new Random(config.getReparationSeed());
		char [] estructura = cromosoma.getEstructura();
		for (int index = 0; index < estructura.length; index++)  {
			 if (estructura[index] == moduleSymbol)
				 contador++;
		}
		diferencia = contador - cromosoma.getNumModulos();
		for (int index = 0; index < diferencia; index++)  {
			 // aqui se controla que no se eliga una posicion que pertenezca a las partes fijas
			 valor = 1 + rnd.nextInt(estructura.length - 2);
			 while (estructura[valor] == variableSymbol)
				 valor = 1 + rnd.nextInt(estructura.length - 2);
			 estructura[valor] = variableSymbol;
		}
	}
	
	/**
	 * Metodo que repara un cromosoma con un error en el inicio de la codificacion
	 * @param cromosoma Cromosoma con errores
	 * */
	
	private static void repararError4(DoubleChromosome cromosoma)  {
		
		// se cambia la primera posicion por un modulo
		char [] estructura = cromosoma.getEstructura();
		char aux = ' ';
		int indice1 = 0;
		for (int indice = 0; indice < estructura.length; indice++)  {
			 if (estructura[indice] == moduleSymbol)  {
				 indice1 = indice;
				 break;
			 }
		}
		aux = estructura[0];
		estructura[0] = estructura[indice1];
		estructura[indice1] = aux;
	}
	
	/**
	 * Metodo que repara un cromosoma con un error en el final de la codificacion
	 * @param cromosoma Cromosoma con errores
	 * */
	
	private static void repararError5(DoubleChromosome cromosoma)  {
		
		// se cambia las dos ultimas posiciones por variable y variable
		char [] estructura = cromosoma.getEstructura();
		int indice1 = -1;
		char aux = ' ';
		for (int index = 1; index < estructura.length; index++)  {
			 if (estructura[index] == variableSymbol)  {
				 indice1 = index;
				 break;
			 }
		}
		aux = estructura[estructura.length - 1];
		estructura[estructura.length - 1] = estructura[indice1];
		estructura[indice1] = aux;
		
		for (int index = 1; index < estructura.length - 1; index++)  {
			 if (estructura[index] == variableSymbol)  {
				 indice1 = index;
				 break;
			 }
		}
		aux = estructura[estructura.length - 2];
		estructura[estructura.length - 2] = estructura[indice1];
		estructura[indice1] = aux;
	}
	
	/**
	 * Metodo que repara un cromosoma con un error en la estructura de la codificacion
	 * @param cromosoma Cromosoma con errores
	 * */
	
	private static void repararError6(DoubleChromosome cromosoma)  {
	
		// cuenta el numero de modulos sumando y restando de forma que solo sea cero la suma al final de la codificacion
		int contador = 2, indice1 = 0, indice2 = 0;
		char [] estructura = cromosoma.getEstructura();
		char aux = ' ';
		while (contador > 0)  {
			 indice1++;
			 if (estructura[indice1] == moduleSymbol)
				 contador++;
			 if (estructura[indice1] == variableSymbol)
				 contador--;
		}
		indice2 = indice1 + 1;
		while (estructura[indice2] != moduleSymbol)
			indice2++;
		
		aux = estructura[indice1];
		estructura[indice1] = estructura[indice2];
		estructura[indice2] = aux;
	}
}
